feat: sistema generico de database migrations com runner Python, auditoria e rollback#116
feat: sistema generico de database migrations com runner Python, auditoria e rollback#116mauriciomendonca wants to merge 6 commits intomainfrom
Conversation
Runner unico (scripts/migrate.py) com CLI via typer para gerenciar migrations SQL e Python. Substitui abordagem ad-hoc de scripts individuais por sistema padronizado. Funcionalidades: - Discovery automatico de migrations por convencao de nome (NNN_desc.sql/py) - Tabela migration_history com auditoria completa (quem, quando, duracao) - Bootstrap automatico: importa schema_version na primeira execucao - Commit atomico: migration + registro de historico na mesma transacao - Comandos CLI: status, migrate, rollback, history, validate - Suporte a --dry-run em todos os comandos destrutivos - Rollback via _rollback.sql (SQL) ou rollback() (Python) 26 testes unitarios cobrindo: discovery, bootstrap, pending, execucao SQL/Python, rollback, validate. Ref: #109 Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
…on 006) Cria scripts/migrations/006_migrate_unique_ids.py seguindo a interface padrao do runner (describe/migrate/rollback). Extrai logica de negocio do script original (slugify, build_id_mapping, batch update) sem argparse ou conexao propria — tudo gerenciado pelo runner. - describe(): retorna descricao humana para logs e auditoria - migrate(conn, dry_run): executa migracao de ~300k unique_ids - rollback(conn, dry_run): restaura IDs MD5 via legacy_unique_id - Funcoes de geracao de ID preservadas identicas ao original Script original (scripts/migrate_unique_ids.py) marcado como DEPRECATED, preservado para backward compatibility com testes existentes. 18 testes unitarios: interface, slugify, suffix, generate_id, migrate, rollback, dry-run, paridade com script original. Ref: #109 Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Rollback files: - 004_create_news_features_rollback.sql: remove tabela, trigger e indices - 005_alter_unique_id_varchar_rollback.sql: reverte VARCHAR(120) para VARCHAR(32), remove legacy_unique_id (requer rollback 006 antes) Schema (init.sql e create_schema.sql): - Adiciona tabela migration_history com auditoria completa - Adiciona view migration_status (estado atual por versao) - Indices em version e started_at para queries de status Ref: #109 Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Substitui inputs hardcoded (migration file, data_migration, batch_size) por interface generica baseada no runner: Inputs: - command: status, migrate, rollback, history, validate - dry_run: preview sem alterar banco (default: true) - target_version: versao alvo para migrate --target ou rollback - confirm: obrigatorio para operacoes destrutivas com dry_run=false Jobs: - validate-inputs: verifica confirmacao para operacoes destrutivas - backup: cria backup automatico antes de migrate/rollback - run-migration: executa scripts/migrate.py com o comando escolhido - show-status: exibe status e historico apos execucao Melhorias: - Cloud SQL Proxy version extraida para env (facilita atualizacao) - Usa vars.GCP_WORKLOAD_IDENTITY_PROVIDER (variavel de org) - environment: production (proteção via required reviewer) Ref: #109 Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
docs/database/migrations.md: reescrito com documentacao completa do novo runner — CLI, discovery, Python migration interface, tabela migration_history, CI/CD workflow. docs/runbooks/migration-rollback.md: novo runbook operacional com 4 cenarios de rollback: - A: Python migration (mais simples) - B: SQL migration com rollback file - C: SQL migration sem rollback file (manual) - D: Rollback multiplo em ordem reversa - Emergencia: restauracao de backup scripts/migrations/README.md: atualizado com referencia ao runner. Ref: #109 Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Code Review — Correções SugeridasRevisão completa do PR. Quatro itens que devem ser corrigidos: 🔴 Problema #4 (ALTO):
|
Correcoes baseadas no code review: - #4 (ALTO): Remover BEGIN/COMMIT explicito do rollback SQL 005 que quebrava o commit atomico do runner (COMMIT prematuro antes do registro em migration_history) - #1 (MEDIO): Remover server-side cursor desnecessario em 006_migrate_unique_ids.py — fetchall() carrega tudo em memoria de qualquer forma, o name= so adiciona confusao semantica - #9 (MEDIO): Sanitizar target_version com regex ^[0-9]{3}$ no workflow e substituir eval $CMD por execucao direta com bash array para prevenir command injection - #5 (BAIXO): Extrair _execute_with_history() em migrate.py para eliminar ~60% de duplicacao entre execute_migration() e execute_rollback(), reduzindo risco de divergencia futura Todos os 44 testes continuam passando. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Summary
Implementa um sistema genérico de migrations para o data-platform, substituindo a abordagem ad-hoc de scripts individuais por um runner Python único com auditoria, dry-run e rollback padronizados.
Motivação: O workflow
db-migrate.yamldo PR #108 é funcional mas hardcoda o scriptmigrate_unique_ids.pye validações específicas. Qualquer migration futura exigiria editar o workflow. Este PR resolve isso criando uma arquitetura reusável.Closes #109
O que muda
1. Runner genérico (
scripts/migrate.py)typer:status,migrate,rollback,history,validatescripts/migrations/por convenção de nome (NNN_desc.sqlouNNN_desc.py)migration_historycom auditoria completa (quem, quando, duração, métricas JSONB)schema_versionna primeira execução--dry-runnativo em todos os comandos destrutivos_rollback.sql(SQL) ourollback()(Python)2. Migration 006 (
scripts/migrations/006_migrate_unique_ids.py)scripts/migrate_unique_ids.pypara a interface do runner (describe/migrate/rollback)3. Rollback SQL files
004_create_news_features_rollback.sql: remove tabela, trigger e índices005_alter_unique_id_varchar_rollback.sql: reverte VARCHAR(120) → VARCHAR(32)4. Schema updates
docker/postgres/init.sqlescripts/create_schema.sql: adicionada tabelamigration_history+ viewmigration_status5. Workflow generalizado (
db-migrate.yaml)command,dry_run,target_version,confirmmigrate_unique_ids.pye validações deunique_idvalidate-inputs→backup→run-migration→show-statusvars.GCP_WORKLOAD_IDENTITY_PROVIDER(variável de org)6. Documentação
docs/database/migrations.md: reescrito com documentação completa do runnerdocs/runbooks/migration-rollback.md: novo runbook com cenários A/B/C/D de rollbackscripts/migrations/README.md: atualizadoArquivos criados/modificados
scripts/migrate.pyscripts/migrations/006_migrate_unique_ids.pyscripts/migrations/004_create_news_features_rollback.sqlscripts/migrations/005_alter_unique_id_varchar_rollback.sqltests/unit/test_migrate_runner.pytests/unit/test_migration_006.pydocs/runbooks/migration-rollback.md.github/workflows/db-migrate.yamldocker/postgres/init.sqlscripts/create_schema.sqlscripts/migrate_unique_ids.pyscripts/migrations/README.mddocs/database/migrations.mdDecisões técnicas
migration_historyvsschema_versionimport, necessitandoimportlib.utilPrincípios seguidos
conninjetado, não criam conexãoBackward-compatible
Este PR pode ser deployado a qualquer momento sem quebrar nada:
migration_historycriada comIF NOT EXISTSschema_versionstatus)Dependências
Test plan
status→migrate --dry-run→migrate→history→rollback --dry-run→validatedb-migrate.yamlcom command=status🤖 Generated with Claude Code